﻿Imports System.Data.OleDb
Imports System.Globalization
Imports System.IO
Imports Microsoft.ML
Imports Microsoft.ML.Data
Imports Microsoft.Office.Interop.Access

Public Class Tera計算EIQAIFrom
＃Region "変数処理"
    ' ========================================
    ' クラスレベル変数
    ' ========================================
    Private mlContext As MLContext
    Private trainedModels As Dictionary(Of String, ITransformer)
    ' 予測エンジンのキャッシュ用
    Private predictionEngines As Dictionary(Of String, PredictionEngine(Of InputData, SinglePrediction))
    Private modelFolder As String
    Private 出力フィールド名リスト As List(Of String)
    ' マトリクス表が有効になっているか（EIQマトリクス表実行Bt が押された後に True）
    Private matrixActive As Boolean = False
    ' T760 テーブルのレコード数 (計算用最大値)
    Private EMax As Integer = 0
    Private IMax As Integer = 0
    ' アイテム数補正の許容差（差がこの値以下なら入力値で上書きする）
    Private Const ITEM_TOLERANCE As Integer = 1

    ' ヘルパー: 文字列を四捨五入して整数文字列にする
    Private Function ToRoundedString(s As String) As String
        If String.IsNullOrWhiteSpace(s) Then
            Return String.Empty
        End If
        Dim d As Double
        If Double.TryParse(s, Globalization.NumberStyles.Any, Globalization.CultureInfo.InvariantCulture, d) Then
            Return CInt(Math.Round(d)).ToString()
        ElseIf Double.TryParse(s, Globalization.NumberStyles.Any, Globalization.CultureInfo.CurrentCulture, d) Then
            Return CInt(Math.Round(d)).ToString()
        Else
            Return String.Empty
        End If
    End Function

    '' T710 テーブル作成: T410 のロジックと同様に乱数に基づいて判定テーブルを作成する
    'Private Sub T710テーブル作成(tableName As String, 採用比率 As Double, seed As Integer)
    '    If String.IsNullOrEmpty(MyDB) OrElse Not File.Exists(MyDB) Then Throw New Exception("DBファイルが見つかりません")

    '    Using conn As New OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={MyDB}")
    '        conn.Open()

    '        ' テーブル削除（存在する場合）
    '        Using cmdDrop As New OleDbCommand($"DROP TABLE [{tableName}]", conn)
    '            Try
    '                cmdDrop.ExecuteNonQuery()
    '            Catch
    '                ' 無視
    '            End Try
    '        End Using

    '        ' 採用比率が1以下ならT410テーブル作成1の方式（乱数を生成）
    '        If 採用比率 <= 1 Then
    '            ' シードに基づく乱数生成で指定件数分のレコードを作成する。
    '            ' 件数は T160 の順位最大値を利用。
    '            Dim cnt As Integer
    '            If tableName.Contains("_E") Then
    '                cnt = GetMaxRankFromTable("T160_全出荷E集計")
    '            Else
    '                cnt = GetMaxRankFromTable("T160_全出荷I集計")
    '            End If

    '            Dim rnd As New Random(seed)

    '            ' 目標件数を算出（比率 * 件数）して、整数で確実にその件数になるようにする
    '            Dim desiredCount As Integer = CInt(Math.Round(採用比率 * cnt))
    '            If desiredCount < 0 Then desiredCount = 0
    '            If desiredCount > cnt Then desiredCount = cnt

    '            ' CREATE TABLE
    '            Dim createSql As String
    '            If tableName.Contains("_E") Then
    '                createSql = $"CREATE TABLE [{tableName}] (順位 AUTOINCREMENT PRIMARY KEY, 乱数 DOUBLE, 判定 VARCHAR(10), 出荷先名 VARCHAR(255))"
    '            Else
    '                createSql = $"CREATE TABLE [{tableName}] (順位 AUTOINCREMENT PRIMARY KEY, 乱数 DOUBLE, 判定 VARCHAR(10), アイテム VARCHAR(255))"
    '            End If
    '            Using cmdCreate As New OleDbCommand(createSql, conn)
    '                cmdCreate.ExecuteNonQuery()
    '            End Using

    '            ' INSERT
    '            Dim insertSql As String = $"INSERT INTO [{tableName}] (乱数, 判定) VALUES (?, ?)"
    '            Using cmdIns As New OleDbCommand(insertSql, conn)
    '                cmdIns.Parameters.Add("@乱数", OleDbType.Double)
    '                cmdIns.Parameters.Add("@判定", OleDbType.VarChar, 10)
    '                For i As Integer = 1 To cnt
    '                    Dim rv As Double = rnd.NextDouble()
    '                    ' deterministic: 上位 desiredCount 件を採用 (Yes) とする
    '                    Dim 判定 As String = If(i <= desiredCount, "Yes", "No")
    '                    cmdIns.Parameters("@乱数").Value = rv
    '                    cmdIns.Parameters("@判定").Value = 判定
    '                    cmdIns.ExecuteNonQuery()
    '                Next
    '            End Using

    '            ' T160 から名称を取り込む
    '            Dim updateSql As String
    '            If tableName.Contains("_E") Then
    '                updateSql = $"UPDATE [{tableName}] LEFT JOIN T160_全出荷E集計 ON [{tableName}].順位 = T160_全出荷E集計.順位 SET [{tableName}].出荷先名 = [T160_全出荷E集計]![出荷先];"
    '            Else
    '                updateSql = $"UPDATE [{tableName}] LEFT JOIN T160_全出荷I集計 ON [{tableName}].順位 = T160_全出荷I集計.順位 SET [{tableName}].アイテム = [T160_全出荷I集計]![アイテム];"
    '            End If
    '            Using cmdUpd As New OleDbCommand(updateSql, conn)
    '                cmdUpd.ExecuteNonQuery()
    '            End Using

    '        Else
    '            ' 採用比率 > 1 の場合は T410作成2 の方式（T160 の乱数をそのまま取り込む）
    '            Dim src As String = If(tableName.Contains("_E"), "T160_全出荷E集計", "T160_全出荷I集計")
    '            Dim fld As String = If(tableName.Contains("_E"), "出荷先", "アイテム")
    '            Dim tname As String = If(tableName.Contains("_E"), "出荷先名", "アイテム")

    '            Dim createSelectSql As String = $"SELECT 順位, 乱数, IIF(乱数 <= {採用比率 - 1}, 'Yes', 'No') AS 判定, {fld} AS {tname} INTO [{tableName}] FROM {src}"
    '            Using cmd As New OleDbCommand(createSelectSql, conn)
    '                cmd.ExecuteNonQuery()
    '            End Using
    '        End If
    '    End Using
    'End Sub

    ' T600 の集計結果を UI の実計算Gb のテキストと 実計算Dgv に反映する
    Private Sub Update実計算表示FromT600()
        If String.IsNullOrEmpty(MyDB) OrElse Not File.Exists(MyDB) Then Return

        Using conn As New OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={MyDB}")
            conn.Open()

            ' T600_手計算 が存在するか確認
            Dim schema = conn.GetSchema("Tables")
            Dim found As Boolean = False
            For Each row As DataRow In schema.Rows
                If row("TABLE_NAME").ToString().ToUpper() = "T600_手計算".ToUpper() Then
                    found = True
                    Exit For
                End If
            Next
            If Not found Then Return

            ' 単一行を取得
            Dim sql As String = "SELECT * FROM T600_手計算"
            Dim dt As New DataTable()
            Using da As New OleDbDataAdapter(sql, conn)
                da.Fill(dt)
            End Using
            If dt.Rows.Count = 0 Then Return

            Dim r As DataRow = dt.Rows(0)

            ' 基本フィールドをテキストボックスに設定
            Try
                TextBox27.Text = If(dt.Columns.Contains("出荷先数") AndAlso Not IsDBNull(r("出荷先数")), Convert.ToDouble(r("出荷先数")).ToString("F0"), "")
                TextBox27.Text = "出荷先数"
            Catch
            End Try
            Try
                TextBox26.Text = "アイテム数"
                実_IAI_Tb.Text = If(dt.Columns.Contains("アイテム数") AndAlso Not IsDBNull(r("アイテム数")), Convert.ToDouble(r("アイテム数")).ToString("F0"), "")
            Catch
            End Try

            ' pred 辞書を作成（予測側のロジックと同様のキー）
            Dim pred As New Dictionary(Of String, String)(StringComparer.OrdinalIgnoreCase)
            ' E1..E5, I1..I5
            For i As Integer = 1 To 5
                Dim ec = $"E{i}"
                If dt.Columns.Contains(ec) AndAlso Not IsDBNull(r(ec)) Then pred(ec) = Convert.ToDouble(r(ec)).ToString("F0") Else pred(ec) = "0"
                Dim ic = $"I{i}"
                If dt.Columns.Contains(ic) AndAlso Not IsDBNull(r(ic)) Then pred(ic) = Convert.ToDouble(r(ic)).ToString("F0") Else pred(ic) = "0"
            Next
            ' 行数01-25, バラ数01-25, ケース01-25, PL01-25, 容積01-25, 重量01-25
            Dim prefixes As String() = {"行数", "バラ数", "ケース", "PL", "容積", "重量"}
            For Each pfx As String In prefixes
                For i As Integer = 1 To 25
                    Dim colName = pfx & i.ToString("D2")
                    If dt.Columns.Contains(colName) AndAlso Not IsDBNull(r(colName)) Then
                        pred(colName) = Convert.ToDouble(r(colName)).ToString("F0")
                    Else
                        pred(colName) = "0"
                    End If
                Next
            Next

            ' 実計算Gb のテキストボックスに E1..E5 を設定（左縦）
            Try
                実_EAI_Tb.Text = If(pred.ContainsKey("E1"), pred("E1"), "")
                実_EA2_Tb.Text = If(pred.ContainsKey("E2"), pred("E2"), "")
                実_EB_Tb.Text = If(pred.ContainsKey("E3"), pred("E3"), "")
                実_EC_Tb.Text = If(pred.ContainsKey("E4"), pred("E4"), "")
                実_ED_Tb.Text = If(pred.ContainsKey("E5"), pred("E5"), "")
                ' 合計（左下）
                Dim sumE As Long = 0
                For i As Integer = 1 To 5
                    sumE += ToLongOrZero(If(pred.ContainsKey($"E{i}"), pred($"E{i}"), "0"))
                Next
                実_EKK_Tb.Text = sumE.ToString()
            Catch
            End Try

            ' 実計算Gb のテキストボックスに I1..I5 を設定（上横）
            Try
                実_IAI_Tb.Text = If(pred.ContainsKey("I1"), pred("I1"), "")
                実_IA2_Tb.Text = If(pred.ContainsKey("I2"), pred("I2"), "")
                実_IB_Tb.Text = If(pred.ContainsKey("I3"), pred("I3"), "")
                実_IC_Tb.Text = If(pred.ContainsKey("I4"), pred("I4"), "")
                実_ID_Tb.Text = If(pred.ContainsKey("I5"), pred("I5"), "")
                Dim sumI As Long = 0
                For i As Integer = 1 To 5
                    sumI += ToLongOrZero(If(pred.ContainsKey($"I{i}"), pred($"I{i}"), "0"))
                Next
                実_IKK_Tb.Text = sumI.ToString()
            Catch
            End Try

            ' マトリクス（実計算Dgv）：AI予測EIQマトリクス表_複写Dgv と同じ単一メトリクス(5x5)表示にする
            If 実計算Dgv Is Nothing Then Return
            実計算Dgv.Rows.Clear()
            実計算Dgv.Columns.Clear()

            ' 表示単位を比較タブのラジオボタンから決定（AI予測と同じ振る舞い）
            Dim prefix As String = ""
            If 抽出_出庫回数2Rb.Checked Then prefix = "行数"
            If 抽出_バラ2Rb.Checked Then prefix = "バラ数"
            If 抽出_ケース2Rb.Checked Then prefix = "ケース"
            If 抽出_PL2Rb.Checked Then prefix = "PL"
            If 抽出_容積2Rb.Checked Then prefix = "容積"
            If 抽出_重量2Rb.Checked Then prefix = "重量"

            ' 列ヘッダーを AI 予測複写 と同じにする
            実計算Dgv.Columns.Add("GPLEランク", "GPLEランク")
            実計算Dgv.Columns.Add("GPLI_A1", "GPLI_A1")
            実計算Dgv.Columns.Add("GPLI_A2", "GPLI_A2")
            実計算Dgv.Columns.Add("GPLI_B", "GPLI_B")
            実計算Dgv.Columns.Add("GPLI_C", "GPLI_C")
            実計算Dgv.Columns.Add("GPLI_D", "GPLI_D")
            実計算Dgv.Columns.Add("横計", "横計")

            ' 列幅調整: 0列は90, others 83
            For i As Integer = 0 To 実計算Dgv.Columns.Count - 1
                If i = 0 Then
                    実計算Dgv.Columns(i).Width = 90
                Else
                    実計算Dgv.Columns(i).Width = 83
                End If
            Next

            ' 行ラベル
            Dim labels As String() = {"GPLE_A1", "GPLE_A2", "GPLE_B", "GPLE_C", "GPLE_D"}
            For Each lbl As String In labels
                実計算Dgv.Rows.Add(New Object() {lbl, "", "", "", "", "", ""})
            Next

            ' 下に合計行を確保
            Const dataRows As Integer = 5
            Dim totalsRowIndex As Integer = dataRows
            While 実計算Dgv.Rows.Count < dataRows + 1
                実計算Dgv.Rows.Add()
            End While

            ' 埋め込み: prefix に基づく 5x5 の値埋め
            Dim lastColIndex As Integer = 実計算Dgv.Columns.Count - 1
            For rr As Integer = 0 To dataRows - 1
                For cc As Integer = 1 To 5
                    Dim idx As Integer = rr * 5 + (cc - 1)
                    Dim k As Integer = idx + 1
                    Dim fieldName As String = prefix & k.ToString("D2")
                    Dim value As String = ""
                    If pred.ContainsKey(fieldName) Then value = pred(fieldName)
                    実計算Dgv.Rows(rr).Cells(cc).Value = value
                Next
            Next

            ' 横計
            For rr As Integer = 0 To dataRows - 1
                Dim rowSum As Long = 0
                For cc As Integer = 1 To 5
                    rowSum += ToLongOrZero(If(実計算Dgv.Rows(rr).Cells(cc).Value, "").ToString())
                Next
                実計算Dgv.Rows(rr).Cells(lastColIndex).Value = rowSum.ToString()
            Next

            ' 縦計
            For cc As Integer = 1 To 5
                Dim colSum As Long = 0
                For rr As Integer = 0 To dataRows - 1
                    colSum += ToLongOrZero(If(実計算Dgv.Rows(rr).Cells(cc).Value, "").ToString())
                Next
                実計算Dgv.Rows(totalsRowIndex).Cells(cc).Value = colSum.ToString()
            Next

            ' 最下行左のラベルと総合計
            実計算Dgv.Rows(totalsRowIndex).Cells(0).Value = "縦計"
            Dim grandTotal As Long = 0
            For c As Integer = 1 To 5
                grandTotal += ToLongOrZero(If(実計算Dgv.Rows(totalsRowIndex).Cells(c).Value, "").ToString())
            Next
            実計算Dgv.Rows(totalsRowIndex).Cells(lastColIndex).Value = grandTotal.ToString()

            ' 書式設定: 右寄せ、ヘッダー中央、読み取り専用
            実計算Dgv.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
            実計算Dgv.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
            For Each col As DataGridViewColumn In 実計算Dgv.Columns
                col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
                col.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter
            Next
            実計算Dgv.ReadOnly = True
            実計算Dgv.RowHeadersVisible = False
        End Using
    End Sub


    ' T160 の順位最大値を取得するためのラッパー（実際はテーブルのレコード数を返す）
    Private Function GetMaxRankFromTable(tableName As String) As Integer
        Try
            If String.IsNullOrEmpty(MyDB) OrElse Not File.Exists(MyDB) Then Return 0

            Using conn As New OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={MyDB}")
                conn.Open()

                ' テーブル存在確認
                Dim schema = conn.GetSchema("Tables")
                Dim found As Boolean = False
                For Each row As DataRow In schema.Rows
                    Dim tname = row("TABLE_NAME").ToString()
                    Dim ttype = row("TABLE_TYPE").ToString()
                    If ttype = "TABLE" AndAlso String.Compare(tname, tableName, True) = 0 Then
                        found = True
                        Exit For
                    End If
                Next
                If Not found Then Return 0

                ' レコード数を取得
                Using cmd As New OleDbCommand($"SELECT COUNT(*) FROM [{tableName}]", conn)
                    Dim result = cmd.ExecuteScalar()
                    If result IsNot Nothing AndAlso Not IsDBNull(result) Then
                        Return CInt(result)
                    End If
                End Using

                Return 0
            End Using
        Catch ex As Exception
            Return 0
        End Try
    End Function

    Private Function ToLongOrZero(s As String) As Long
        Dim v As Long
        If Long.TryParse(s, v) Then
            Return v
        End If
        Return 0
    End Function

    ' DBヘルパー: 指定テーブルの数値カラムの最大値を取得する（使用されていない - 将来用に保持）
    Private Function GetMaxNumericValueFromTable(tableName As String) As Integer
        Try
            If String.IsNullOrEmpty(MyDB) OrElse Not File.Exists(MyDB) Then Return 0

            Using conn As New OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={MyDB}")
                conn.Open()

                ' テーブル存在確認
                Dim schema = conn.GetSchema("Tables")
                Dim found As Boolean = False
                For Each row As DataRow In schema.Rows
                    Dim tname = row("TABLE_NAME").ToString()
                    Dim ttype = row("TABLE_TYPE").ToString()
                    If ttype = "TABLE" AndAlso String.Compare(tname, tableName, True) = 0 Then
                        found = True
                        Exit For
                    End If
                Next
                If Not found Then Return 0

                Dim da As New OleDbDataAdapter($"SELECT * FROM [{tableName}]", conn)
                Dim dt As New DataTable()
                da.Fill(dt)
                If dt.Rows.Count = 0 Then Return 0

                Dim maxVal As Double = Double.MinValue
                For Each col As DataColumn In dt.Columns
                    Dim t = col.DataType
                    If t Is GetType(Integer) OrElse t Is GetType(Long) OrElse t Is GetType(Double) OrElse t Is GetType(Single) OrElse t Is GetType(Decimal) Then
                        For Each r As DataRow In dt.Rows
                            If Not IsDBNull(r(col)) Then
                                Dim v As Double = Convert.ToDouble(r(col))
                                If v > maxVal Then maxVal = v
                            End If
                        Next
                    End If
                Next
                If maxVal = Double.MinValue Then Return 0
                Return CInt(Math.Round(maxVal))
            End Using
        Catch ex As Exception
            Return 0
        End Try
    End Function

    ' データ構造定義
    Public Class ModelData
        <LoadColumn(0)> Public モデル連番 As Single
        <LoadColumn(1)> Public 出荷先数 As Single
        <LoadColumn(2)> Public アイテム数 As Single
        <LoadColumn(3)> Public バラ数量 As Single
        <LoadColumn(4)> Public 行数 As Single
        <LoadColumn(5)> Public E1 As Single
        <LoadColumn(6)> Public E2 As Single
        <LoadColumn(7)> Public E3 As Single
        <LoadColumn(8)> Public E4 As Single
        <LoadColumn(9)> Public E5 As Single
        <LoadColumn(10)> Public I1 As Single
        <LoadColumn(11)> Public I2 As Single
        <LoadColumn(12)> Public I3 As Single
        <LoadColumn(13)> Public I4 As Single
        <LoadColumn(14)> Public I5 As Single
        ' 行数01-25, バラ数01-25, ケース01-25, PL01-25, 容積01-25, 重量01-25も同様に定義
        ' (省略：必要に応じて追加)
    End Class

    ' 入力データ構造
    Public Class InputData
        Public 出荷先数 As Single
        Public アイテム数 As Single
    End Class

    ' 単一予測結果クラス
    Public Class SinglePrediction
        <ColumnName("Score")>
        Public Score As Single
    End Class
#End Region

#Region "画面操作"
    ' ========================================
    ' フォームロード
    ' ========================================
    Private Sub Tera計算EIQAI_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        フォルダー確認()

        MyDB = Dat保存Path & "\EIQ_AIモデル.accdb"
        Dim Saizu As Integer = 500 ' MB
        Access最適化(MyDB, Saizu)

        Try
            ' MLContextを初期化
            mlContext = New MLContext(seed:=0)
            modelFolder = Dat保存Path
            trainedModels = New Dictionary(Of String, ITransformer)
            predictionEngines = New Dictionary(Of String, PredictionEngine(Of InputData, SinglePrediction))

            ' モデルフォルダの確認
            If Not Directory.Exists(modelFolder) Then
                MsgBox("学習済モデルフォルダが見つかりません。" & vbCrLf &
                      $"フォルダ: {modelFolder}" & vbCrLf & vbCrLf &
                      "モデルファイル(.zip)を上記フォルダに配置してください。",
                      MsgBoxStyle.Exclamation, "フォルダ未検出")
            End If

            出力フィールド名リスト初期化()   ' 出力フィールド名リストを初期化
            学習済モデル読込()               ' 自動的にモデルを読み込む
            AI予測EIQマトリクス表Dgv初期化() ' AI予測EIQマトリクス表Dgvのヘッダー初期化

            ' 比較開始ボタンが押されるまで、表示チェック2Gbのラジオボタンを無効にする
            Set表示チェック2GbEnabled(False)

            Debug.WriteLine("modelFolder=" & modelFolder)
            Debug.WriteLine("MyDB=" & MyDB)

        Catch ex As Exception
            MsgBox($"初期化エラー: {ex.Message}", MsgBoxStyle.Critical, "エラー")
        End Try
    End Sub
    ' ========================================
    ' 学習済モデル読込ボタン
    ' ========================================
    Private Async Sub 学習済モデル読込Bt_Click(sender As Object, e As EventArgs) Handles 学習済モデル読込Bt.Click
        学習済モデル読込Bt.Enabled = False
        モデル状態Lb.Text = "モデル: 読込中..."
        モデル状態Lb.ForeColor = Color.Orange
        Try
            Await Task.Run(Sub() 学習済モデル読込())
            If trainedModels.Count > 0 Then
                MsgBox($"{trainedModels.Count} 個のモデルを読み込みました。",
                      MsgBoxStyle.Information, "読込完了")
            End If
        Finally
            学習済モデル読込Bt.Enabled = True
        End Try
    End Sub
    ' ========================================
    ' 予測実行ボタン
    ' ========================================
    Private Async Sub 予測実行Bt_Click(sender As Object, e As EventArgs) Handles 予測実行Bt.Click
        予測実行Bt.Enabled = False
        Try
            出荷先数2Tb.Text = 出荷先数Tb.Text
            アイテム数2Tb.Text = アイテム数Tb.Text
            Await 予測実行()
        Finally
            予測実行Bt.Enabled = True
        End Try
    End Sub
    Private Sub EIQマトリクス表実行Bt_Click(sender As Object, e As EventArgs) Handles EIQマトリクス表実行Bt.Click
        AI予測EIQマトリクス表()
        AI予測従来計算E（)
        AI予測従来計算I（)
        matrixActive = True
    End Sub

    ' 表示単位のラジオボタンが変更されたら、マトリクスが有効な場合に再描画する
    Private Sub 抽出_表示単位_CheckedChanged(sender As Object, e As EventArgs) Handles 抽出_出庫回数Rb.CheckedChanged, 抽出_バラRb.CheckedChanged, 抽出_ケースRb.CheckedChanged, 抽出_PLRb.CheckedChanged, 抽出_容積Rb.CheckedChanged, 抽出_重量Rb.CheckedChanged
        If matrixActive Then
            AI予測EIQマトリクス表()
        End If
    End Sub
    Private Sub AI予測と実計算比較移動Bt_Click(sender As Object, e As EventArgs) Handles AI予測と実計算比較移動Bt.Click
        Tera計算EIQAI_Tc.SelectedTab = AI予測と実計算比較Tp
        比較画面クリア()
    End Sub
    Private Sub 比較開始Bt_Click(sender As Object, e As EventArgs) Handles 比較開始Bt.Click
        '予測計算2Gbを表示（予測計算1Gbと同じ内容)
        予測計算Gb作成()
        Set表示チェック2GbEnabled(True)

        EMax = GetMaxRankFromTable("T160_全出荷E集計")
        IMax = GetMaxRankFromTable("T160_全出荷I集計")

        Dim 計算E数 = CInt(出荷先数2Tb.Text)
        Dim E採用判定値 As Double = CDbl(計算E数 / EMax)
        '  IMaxはT760_全出荷I集計 'T160からT760作成()で取得
        Dim 計算I数 = CInt(アイテム数Tb.Text)
        Dim I採用判定値 As Double = CDbl(計算I数 / IMax)

        MyTable = "T800_計算対象"

        T350_計算対象1(E採用判定値)
        T350_計算対象2(I採用判定値)
        T360_全出荷E集計()
        T360_全出荷I集計()
        T400_クロス集計()

        T600_手計算()
        'T600_手計算作成()
        Update実計算表示FromT600()

        誤差表示()
    End Sub

    ' 指定した enabled 値で比較タブ側の表示単位ラジオボタン群を有効/無効化する
    Private Sub Set表示チェック2GbEnabled(enabled As Boolean)
        Try
            抽出_出庫回数2Rb.Enabled = enabled
            抽出_バラ2Rb.Enabled = enabled
            抽出_ケース2Rb.Enabled = enabled
            抽出_PL2Rb.Enabled = enabled
            抽出_容積2Rb.Enabled = enabled
            抽出_重量2Rb.Enabled = enabled
        Catch
            ' Designerでコントロールが未定義の場合は無視
        End Try
    End Sub
    Private Sub EI変更Tb_Click(sender As Object, e As EventArgs) Handles EI変更Tb.Click
        ' 1番目（インデックス0）のタブに移動
        Tera計算EIQAI_Tc.SelectedTab = Tera計算EIQAI_Tc.TabPages("AI予測計算Tp")
    End Sub
    Private Sub 比較画面クリア()
        Try
            ' --- 1. 描画を一時停止してチラつきを抑える ---
            Me.SuspendLayout()

            ' --- 2. テキストボックスのクリア ---
            Dim tbNames As String() = {
                "AI_IAI_Tb", "AI_IA2_Tb", "AI_IB_Tb", "AI_IC_Tb", "AI_ID_Tb", "AI_IKK_Tb",
                "AI_EAI_Tb", "AI_EA2_Tb", "AI_EB_Tb", "AI_EC_Tb", "AI_ED_Tb", "AI_EKK_Tb",
                "実_EAI_Tb", "実_EA2_Tb", "実_EB_Tb", "実_EC_Tb", "実_ED_Tb", "実_EKK_Tb",
                "実_IAI_Tb", "実_IA2_Tb", "実_IB_Tb", "実_IC_Tb", "実_ID_Tb", "実_IKK_Tb",
                "比_EAI_Tb", "比_EA2_Tb", "比_EB_Tb", "比_EC_Tb", "比_ED_Tb", "比_EKK_Tb",
                "比_IAI_Tb", "比_IA2_Tb", "比_IB_Tb", "比_IC_Tb", "比_ID_Tb", "比_IKK_Tb"
            }

            For Each name As String In tbNames
                Dim ctrl As Control() = Me.Controls.Find(name, True)
                If ctrl.Length > 0 AndAlso TypeOf ctrl(0) Is TextBox Then
                    DirectCast(ctrl(0), TextBox).Clear() ' Text="" より Clear() の方が明示的
                End If

            Next

            ' --- 3. DataGridViewのクリア ---
            AI予測従来計算アイテムDgv.Columns.Clear()
            実計算Dgv.Columns.Clear()
            誤差Dgv.Columns.Clear()

            ' --- 4. フラグのリセット ---
            matrixActive = False

            ' --- 5. レイアウトを再開して強制的に反映 ---
            Me.ResumeLayout()
            Me.Refresh() ' これでフォーム全体の描画を強制実行

        Catch
            ' クリア中の例外は無視
        End Try
    End Sub

    ' T160 から T760 を作成し、EMax/IMax（T760 のレコード数）を設定する
    Public Sub T160からT760作成()
        Try
            If String.IsNullOrEmpty(MyDB) OrElse Not File.Exists(MyDB) Then
                Throw New Exception("DBファイルが見つかりません")
            End If

            Using conn As New OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={MyDB}")
                conn.Open()

                ' 既存の T760 テーブルを削除（存在する場合）
                Try
                    Using cmdDrop As New OleDbCommand("DROP TABLE [T760_全出荷E集計]", conn)
                        cmdDrop.ExecuteNonQuery()
                    End Using
                Catch
                End Try
                Try
                    Using cmdDrop As New OleDbCommand("DROP TABLE [T760_全出荷I集計]", conn)
                        cmdDrop.ExecuteNonQuery()
                    End Using
                Catch
                End Try

                ' T160 テーブルの存在確認
                Dim schema = conn.GetSchema("Tables")
                Dim hasT160E As Boolean = False
                Dim hasT160I As Boolean = False
                For Each row As DataRow In schema.Rows
                    Dim tname = row("TABLE_NAME").ToString()
                    Dim ttype = row("TABLE_TYPE").ToString()
                    If ttype = "TABLE" Then
                        If String.Compare(tname, "T160_全出荷E集計", True) = 0 Then hasT160E = True
                        If String.Compare(tname, "T160_全出荷I集計", True) = 0 Then hasT160I = True
                    End If
                Next

                ' 存在する場合はコピー（SELECT INTO）で T760 を作成
                If hasT160E Then
                    Using cmd As New OleDbCommand("SELECT * INTO T760_全出荷E集計 FROM T160_全出荷E集計", conn)
                        Try
                            cmd.ExecuteNonQuery()
                        Catch
                        End Try
                    End Using
                End If

                If hasT160I Then
                    Using cmd As New OleDbCommand("SELECT * INTO T760_全出荷I集計 FROM T160_全出荷I集計", conn)
                        Try
                            cmd.ExecuteNonQuery()
                        Catch
                        End Try
                    End Using
                End If

                ' レコード数を取得して EMax/IMax に格納
                Try
                    If hasT160E Then
                        Using cmd As New OleDbCommand("SELECT COUNT(*) FROM T760_全出荷E集計", conn)
                            Dim cnt = cmd.ExecuteScalar()
                            If cnt IsNot Nothing AndAlso Not IsDBNull(cnt) Then
                                EMax = CInt(cnt)
                            Else
                                EMax = 0
                            End If
                        End Using
                    Else
                        EMax = 0
                    End If
                Catch
                    EMax = 0
                End Try

                Try
                    If hasT160I Then
                        Using cmd As New OleDbCommand("SELECT COUNT(*) FROM T760_全出荷I集計", conn)
                            Dim cnt = cmd.ExecuteScalar()
                            If cnt IsNot Nothing AndAlso Not IsDBNull(cnt) Then
                                IMax = CInt(cnt)
                            Else
                                IMax = 0
                            End If
                        End Using
                    Else
                        IMax = 0
                    End If
                Catch
                    IMax = 0
                End Try

            End Using

        Catch ex As Exception
            ' 失敗した場合は最大値を0に設定
            EMax = 0
            IMax = 0
        End Try
    End Sub

    Private Sub 誤差表示()
        Try
            ' 必要なDataGridViewが存在するかチェック
            If 実計算Dgv Is Nothing OrElse AI予測EIQマトリクス表_複写Dgv Is Nothing OrElse 誤差Dgv Is Nothing Then
                Return
            End If

            ' マトリクスは 5x5 (データ行) + 合計行 で扱う
            Const dataRows As Integer = 5
            Const dataCols As Integer = 5

            ' 解析用のパーサ
            Dim parse As Func(Of String, Double) = Function(s As String) As Double
                                                       Dim d As Double
                                                       If String.IsNullOrWhiteSpace(s) Then Return 0.0
                                                       If Double.TryParse(s, Globalization.NumberStyles.Any, CultureInfo.CurrentCulture, d) Then Return d
                                                       If Double.TryParse(s, Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, d) Then Return d
                                                       Return 0.0
                                                   End Function

            ' 誤差Dgv の初期化（列ヘッダーは実・予測と同じ形式）
            誤差Dgv.Columns.Clear()
            誤差Dgv.Columns.Add("GPLEランク", "GPLEランク")
            誤差Dgv.Columns.Add("GPLI_A1", "GPLI_A1")
            誤差Dgv.Columns.Add("GPLI_A2", "GPLI_A2")
            誤差Dgv.Columns.Add("GPLI_B", "GPLI_B")
            誤差Dgv.Columns.Add("GPLI_C", "GPLI_C")
            誤差Dgv.Columns.Add("GPLI_D", "GPLI_D")
            誤差Dgv.Columns.Add("横計", "横計")

            ' 列幅設定
            For i As Integer = 0 To 誤差Dgv.Columns.Count - 1
                If i = 0 Then
                    誤差Dgv.Columns(i).Width = 90
                Else
                    誤差Dgv.Columns(i).Width = 83
                End If
            Next

            ' 行ラベルを作成 (データ行 + 合計行)
            誤差Dgv.Rows.Clear()
            Dim labels As String() = {"GPLE_A1", "GPLE_A2", "GPLE_B", "GPLE_C", "GPLE_D"}
            For Each lbl As String In labels
                誤差Dgv.Rows.Add(New Object() {lbl, "", "", "", "", "", ""})
            Next
            ' 合計行を確保
            While 誤差Dgv.Rows.Count < dataRows + 1
                誤差Dgv.Rows.Add()
            End While
            Dim lastColIndex As Integer = 誤差Dgv.Columns.Count - 1
            Dim totalsRowIndex As Integer = dataRows

            ' セルごとに誤差を計算: (予測 - 実) / 実 * 100
            For r As Integer = 0 To dataRows - 1
                For c As Integer = 1 To dataCols
                    Dim actualStr As String = If(実計算Dgv.Rows.Count > r AndAlso 実計算Dgv.Rows(r).Cells.Count > c AndAlso 実計算Dgv.Rows(r).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(r).Cells(c).Value.ToString(), "")
                    Dim predStr As String = If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > r AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value.ToString(), "")
                    Dim actual As Double = parse(actualStr)
                    Dim pred As Double = parse(predStr)

                    Dim pctText As String
                    If actual = 0 Then
                        If pred = 0 Then
                            pctText = "0.00%"
                        Else
                            pctText = "N/A"
                        End If
                    Else
                        Dim pct As Double = (pred - actual) / Math.Abs(actual) * 100.0
                        pctText = pct.ToString("F2") & "%"

                    End If

                    誤差Dgv.Rows(r).Cells(c).Value = pctText
                Next

                ' 行の横計(合計値の誤差): 実と予測の行合計で計算
                Dim actualRowSum As Double = 0
                Dim predRowSum As Double = 0
                For c As Integer = 1 To dataCols
                    actualRowSum += parse(If(実計算Dgv.Rows.Count > r AndAlso 実計算Dgv.Rows(r).Cells.Count > c AndAlso 実計算Dgv.Rows(r).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                    predRowSum += parse(If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > r AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                Next
                Dim rowPct As String
                If actualRowSum = 0 Then
                    If predRowSum = 0 Then
                        rowPct = "0.00%"
                    Else
                        rowPct = "N/A"
                    End If
                Else
                    rowPct = ((predRowSum - actualRowSum) / Math.Abs(actualRowSum) * 100.0).ToString("F2") & "%"

                End If
                誤差Dgv.Rows(r).Cells(lastColIndex).Value = rowPct
            Next

            ' 縦計（各列）の誤差を下段に表示
            For c As Integer = 1 To dataCols
                Dim actualColSum As Double = 0
                Dim predColSum As Double = 0
                For r As Integer = 0 To dataRows - 1
                    actualColSum += parse(If(実計算Dgv.Rows.Count > r AndAlso 実計算Dgv.Rows(r).Cells.Count > c AndAlso 実計算Dgv.Rows(r).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                    predColSum += parse(If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > r AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                Next
                Dim colPct As String
                If actualColSum = 0 Then
                    If predColSum = 0 Then
                        colPct = "0.00%"
                    Else
                        colPct = "N/A"
                    End If
                Else
                    colPct = ((predColSum - actualColSum) / Math.Abs(actualColSum) * 100.0).ToString("F2") & "%"

                End If
                誤差Dgv.Rows(totalsRowIndex).Cells(c).Value = colPct
            Next

            ' 最下行左のラベルと総合合計の誤差
            誤差Dgv.Rows(totalsRowIndex).Cells(0).Value = "縦計"
            Dim actualGrand As Double = 0
            Dim predGrand As Double = 0
            For c As Integer = 1 To 5
                Dim aVal As String = If(実計算Dgv.Rows.Count > totalsRowIndex AndAlso 実計算Dgv.Rows(totalsRowIndex).Cells.Count > c AndAlso 実計算Dgv.Rows(totalsRowIndex).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(totalsRowIndex).Cells(c).Value.ToString(), "")
                Dim pVal As String = If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > totalsRowIndex AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells(c).Value.ToString(), "")
                actualGrand += parse(aVal)
                predGrand += parse(pVal)
            Next
            Dim grandPct As String
            If actualGrand = 0 Then
                If predGrand = 0 Then
                    grandPct = "0.00%"
                Else
                    grandPct = "N/A"
                End If
            Else
                grandPct = ((predGrand - actualGrand) / Math.Abs(actualGrand) * 100.0).ToString("F2") & "%"

            End If
            誤差Dgv.Rows(totalsRowIndex).Cells(lastColIndex).Value = grandPct

            ' --- 出荷先数 / アイテム数 の％計算を誤差計算Gbのテキストボックスに表示 ---
            Try
                ' E1..E5
                Dim actualEs() As String = {実_EAI_Tb.Text, 実_EA2_Tb.Text, 実_EB_Tb.Text, 実_EC_Tb.Text, 実_ED_Tb.Text}
                Dim predEs() As String = {AI_EAI_Tb.Text, AI_EA2_Tb.Text, AI_EB_Tb.Text, AI_EC_Tb.Text, AI_ED_Tb.Text}
                Dim 比Es() As TextBox = {比_EAI_Tb, 比_EA2_Tb, 比_EB_Tb, 比_EC_Tb, 比_ED_Tb}
                For i As Integer = 0 To 4
                    Dim a As Double = parse(If(actualEs(i), ""))
                    Dim p As Double = parse(If(predEs(i), ""))
                    Dim t As String
                    If a = 0 Then
                        If p = 0 Then
                            t = "0.00%"
                        Else
                            t = "N/A"
                        End If
                    Else
                        t = ((p - a) / Math.Abs(a) * 100.0).ToString("F2") & "%"
                    End If
                    比Es(i).Text = t
                Next

                ' I1..I5
                Dim actualIs() As String = {実_IAI_Tb.Text, 実_IA2_Tb.Text, 実_IB_Tb.Text, 実_IC_Tb.Text, 実_ID_Tb.Text}
                Dim predIs() As String = {AI_IAI_Tb.Text, AI_IA2_Tb.Text, AI_IB_Tb.Text, AI_IC_Tb.Text, AI_ID_Tb.Text}
                Dim 比Is() As TextBox = {比_IAI_Tb, 比_IA2_Tb, 比_IB_Tb, 比_IC_Tb, 比_ID_Tb}
                For i As Integer = 0 To 4
                    Dim a As Double = parse(If(actualIs(i), ""))
                    Dim p As Double = parse(If(predIs(i), ""))
                    Dim t As String
                    If a = 0 Then
                        If p = 0 Then
                            t = "0.00%"
                        Else
                            t = "N/A"
                        End If
                    Else
                        t = ((p - a) / Math.Abs(a) * 100.0).ToString("F2") & "%"
                    End If
                    比Is(i).Text = t
                Next

                ' 合計（出荷先数・アイテム数）
                Dim actualETotal As Double = parse(実_EKK_Tb.Text)
                Dim predETotal As Double = parse(AI_EKK_Tb.Text)
                If actualETotal = 0 Then
                    If predETotal = 0 Then
                        比_EKK_Tb.Text = "0.00%"
                    Else
                        比_EKK_Tb.Text = "N/A"
                    End If
                Else
                    比_EKK_Tb.Text = ((predETotal - actualETotal) / Math.Abs(actualETotal) * 100.0).ToString("F2") & "%"
                End If

                Dim actualITotal As Double = parse(実_IKK_Tb.Text)
                Dim predITotal As Double = parse(AI_IKK_Tb.Text)
                If actualITotal = 0 Then
                    If predITotal = 0 Then
                        比_IKK_Tb.Text = "0.00%"
                    Else
                        比_IKK_Tb.Text = "N/A"
                    End If
                Else
                    比_IKK_Tb.Text = ((predITotal - actualITotal) / Math.Abs(actualITotal) * 100.0).ToString("F2") & "%"
                End If
            Catch
                ' 表示更新時の例外は無視
            End Try

            ' 書式設定
            誤差Dgv.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
            誤差Dgv.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
            For Each col As DataGridViewColumn In 誤差Dgv.Columns
                col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
                col.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter
            Next
            誤差Dgv.ReadOnly = True
            誤差Dgv.RowHeadersVisible = False

        Catch ex As Exception
            ' UI更新中のエラーは無視
        End Try
    End Sub

    Private Sub 誤差表示1()
        Try
            ' 必要なDataGridViewが存在するかチェック
            If 実計算Dgv Is Nothing OrElse AI予測EIQマトリクス表_複写Dgv Is Nothing OrElse 誤差Dgv Is Nothing Then
                Return
            End If

            ' マトリクスは 5x5 (データ行) + 合計行 で扱う
            Const dataRows As Integer = 5
            Const dataCols As Integer = 5

            ' 解析用のパーサ
            Dim parse As Func(Of String, Double) = Function(s As String) As Double
                                                       Dim d As Double
                                                       If String.IsNullOrWhiteSpace(s) Then Return 0.0
                                                       If Double.TryParse(s, Globalization.NumberStyles.Any, CultureInfo.CurrentCulture, d) Then Return d
                                                       If Double.TryParse(s, Globalization.NumberStyles.Any, CultureInfo.InvariantCulture, d) Then Return d
                                                       Return 0.0
                                                   End Function

            ' 誤差Dgv の初期化（列ヘッダーは実・予測と同じ形式）
            誤差Dgv.Columns.Clear()
            誤差Dgv.Columns.Add("GPLEランク", "GPLEランク")
            誤差Dgv.Columns.Add("GPLI_A1", "GPLI_A1")
            誤差Dgv.Columns.Add("GPLI_A2", "GPLI_A2")
            誤差Dgv.Columns.Add("GPLI_B", "GPLI_B")
            誤差Dgv.Columns.Add("GPLI_C", "GPLI_C")
            誤差Dgv.Columns.Add("GPLI_D", "GPLI_D")
            誤差Dgv.Columns.Add("横計", "横計")

            ' 列幅設定
            For i As Integer = 0 To 誤差Dgv.Columns.Count - 1
                If i = 0 Then
                    誤差Dgv.Columns(i).Width = 90
                Else
                    誤差Dgv.Columns(i).Width = 83
                End If
            Next

            ' 行ラベルを作成 (データ行 + 合計行)
            誤差Dgv.Rows.Clear()
            Dim labels As String() = {"GPLE_A1", "GPLE_A2", "GPLE_B", "GPLE_C", "GPLE_D"}
            For Each lbl As String In labels
                誤差Dgv.Rows.Add(New Object() {lbl, "", "", "", "", "", ""})
            Next
            ' 合計行を確保
            While 誤差Dgv.Rows.Count < dataRows + 1
                誤差Dgv.Rows.Add()
            End While

            Dim lastColIndex As Integer = 誤差Dgv.Columns.Count - 1
            Dim totalsRowIndex As Integer = dataRows

            ' セルごとに誤差を計算: (予測 - 実) / 実 * 100
            For r As Integer = 0 To dataRows - 1
                For c As Integer = 1 To dataCols
                    Dim actualStr As String = If(実計算Dgv.Rows.Count > r AndAlso 実計算Dgv.Rows(r).Cells.Count > c AndAlso 実計算Dgv.Rows(r).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(r).Cells(c).Value.ToString(), "")
                    Dim predStr As String = If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > r AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value.ToString(), "")
                    Dim actual As Double = parse(actualStr)
                    Dim pred As Double = parse(predStr)

                    Dim pctText As String
                    If actual = 0 Then
                        If pred = 0 Then
                            pctText = "0.00%"
                        Else
                            pctText = "N/A"
                        End If
                    Else
                        Dim pct As Double = (pred - actual) / Math.Abs(actual) * 100.0
                        pctText = pct.ToString("F2") & "%"

                    End If

                    誤差Dgv.Rows(r).Cells(c).Value = pctText
                Next

                ' 行の横計(合計値の誤差): 実と予測の行合計で計算
                Dim actualRowSum As Double = 0
                Dim predRowSum As Double = 0
                For c As Integer = 1 To dataCols
                    actualRowSum += parse(If(実計算Dgv.Rows.Count > r AndAlso 実計算Dgv.Rows(r).Cells.Count > c AndAlso 実計算Dgv.Rows(r).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                    predRowSum += parse(If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > r AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                Next
                Dim rowPct As String
                If actualRowSum = 0 Then
                    If predRowSum = 0 Then
                        rowPct = "0.00%"
                    Else
                        rowPct = "N/A"
                    End If
                Else
                    rowPct = ((predRowSum - actualRowSum) / Math.Abs(actualRowSum) * 100.0).ToString("F2") & "%"

                End If
                誤差Dgv.Rows(r).Cells(lastColIndex).Value = rowPct
            Next

            ' 縦計（各列）の誤差を下段に表示
            For c As Integer = 1 To dataCols
                Dim actualColSum As Double = 0
                Dim predColSum As Double = 0
                For r As Integer = 0 To dataRows - 1
                    actualColSum += parse(If(実計算Dgv.Rows.Count > r AndAlso 実計算Dgv.Rows(r).Cells.Count > c AndAlso 実計算Dgv.Rows(r).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                    predColSum += parse(If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > r AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value.ToString(), ""))
                Next
                Dim colPct As String
                If actualColSum = 0 Then
                    If predColSum = 0 Then
                        colPct = "0.00%"
                    Else
                        colPct = "N/A"
                    End If
                Else
                    colPct = ((predColSum - actualColSum) / Math.Abs(actualColSum) * 100.0).ToString("F2") & "%"

                End If
                誤差Dgv.Rows(totalsRowIndex).Cells(c).Value = colPct
            Next

            ' 最下行左のラベルと総合合計の誤差
            誤差Dgv.Rows(totalsRowIndex).Cells(0).Value = "縦計"
            Dim actualGrand As Double = 0
            Dim predGrand As Double = 0
            For c As Integer = 1 To 5
                Dim aVal As String = If(実計算Dgv.Rows.Count > totalsRowIndex AndAlso 実計算Dgv.Rows(totalsRowIndex).Cells.Count > c AndAlso 実計算Dgv.Rows(totalsRowIndex).Cells(c).Value IsNot Nothing, 実計算Dgv.Rows(totalsRowIndex).Cells(c).Value.ToString(), "")
                Dim pVal As String = If(AI予測EIQマトリクス表_複写Dgv.Rows.Count > totalsRowIndex AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells.Count > c AndAlso AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells(c).Value IsNot Nothing, AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells(c).Value.ToString(), "")
                actualGrand += parse(aVal)
                predGrand += parse(pVal)
            Next
            Dim grandPct As String
            If actualGrand = 0 Then
                If predGrand = 0 Then
                    grandPct = "0.00%"
                Else
                    grandPct = "N/A"
                End If
            Else
                grandPct = ((predGrand - actualGrand) / Math.Abs(actualGrand) * 100.0).ToString("F2") & "%"

            End If
            誤差Dgv.Rows(totalsRowIndex).Cells(lastColIndex).Value = grandPct

            ' 書式設定
            誤差Dgv.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
            誤差Dgv.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
            For Each col As DataGridViewColumn In 誤差Dgv.Columns
                col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
                col.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter
            Next
            誤差Dgv.ReadOnly = True
            誤差Dgv.RowHeadersVisible = False

        Catch ex As Exception
            ' UI更新中のエラーは無視
        End Try
    End Sub

    Private Sub T600_手計算作成()
        If String.IsNullOrEmpty(MyDB) OrElse Not File.Exists(MyDB) Then
            Throw New Exception("DBファイルが見つかりません")
        End If

        Using conn As New OleDbConnection($"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={MyDB}")
            conn.Open()

            Try
                ' === T800_計算対象 の存在確認 ===
                Dim schema = conn.GetSchema("Tables")
                Dim hasT800 As Boolean = False
                For Each row As DataRow In schema.Rows
                    If row("TABLE_NAME").ToString().ToUpper() = "T800_計算対象" AndAlso row("TABLE_TYPE").ToString() = "TABLE" Then
                        hasT800 = True
                        Exit For
                    End If
                Next

                If Not hasT800 Then
                    MsgBox("T800_計算対象テーブルが存在しません。" & vbCrLf & "比較開始ボタンを押す前に、先にデータを準備してください。", MsgBoxStyle.Exclamation, "テーブル未作成")
                    Return
                End If

                ' === E1-E5 (出荷先数 by GPLEランク) ===
                Try
                    Using cmdDrop As New OleDbCommand("DROP TABLE T590_E出荷先一時", conn)
                        cmdDrop.ExecuteNonQuery()
                    End Using
                Catch
                End Try

                Using cmdCreateTempE As New OleDbCommand("SELECT GPLEランク, 出荷先 INTO T590_E出荷先一時 FROM T800_計算対象 GROUP BY GPLEランク, 出荷先", conn)
                    cmdCreateTempE.ExecuteNonQuery()
                End Using

                Dim E1 As Double = 0, E2 As Double = 0, E3 As Double = 0, E4 As Double = 0, E5 As Double = 0
                Using cmdE As New OleDbCommand("SELECT GPLEランク, Count(出荷先) AS 出荷先数 FROM T590_E出荷先一時 GROUP BY GPLEランク", conn)
                    Using readerE As OleDbDataReader = cmdE.ExecuteReader()
                        While readerE.Read()
                            If Not IsDBNull(readerE("GPLEランク")) AndAlso Not IsDBNull(readerE("出荷先数")) Then
                                Dim ランク As String = readerE("GPLEランク").ToString()
                                Dim 数 As Double = Convert.ToDouble(readerE("出荷先数"))
                                Select Case ランク
                                    Case "GPLE_A1" : E1 = 数
                                    Case "GPLE_A2" : E2 = 数
                                    Case "GPLE_B" : E3 = 数
                                    Case "GPLE_C" : E4 = 数
                                    Case "GPLE_D" : E5 = 数
                                End Select
                            End If
                        End While
                    End Using
                End Using

                ' drop temp
                Try
                    Using cmdDrop As New OleDbCommand("DROP TABLE T590_E出荷先一時", conn)
                        cmdDrop.ExecuteNonQuery()
                    End Using
                Catch
                End Try

                ' === I1-I5 (アイテム数 by GPLIランク) ===
                Try
                    Using cmdDropI As New OleDbCommand("DROP TABLE T590_Iアイテム一時", conn)
                        cmdDropI.ExecuteNonQuery()
                    End Using
                Catch
                End Try

                Using cmdCreateTempI As New OleDbCommand("SELECT GPLIランク, アイテム INTO T590_Iアイテム一時 FROM T800_計算対象 GROUP BY GPLIランク, アイテム", conn)
                    cmdCreateTempI.ExecuteNonQuery()
                End Using

                Dim I1 As Double = 0, I2 As Double = 0, I3 As Double = 0, I4 As Double = 0, I5 As Double = 0
                Using cmdI As New OleDbCommand("SELECT GPLIランク, Count(アイテム) AS アイテム数 FROM T590_Iアイテム一時 GROUP BY GPLIランク", conn)
                    Using readerI As OleDbDataReader = cmdI.ExecuteReader()
                        While readerI.Read()
                            If Not IsDBNull(readerI("GPLIランク")) AndAlso Not IsDBNull(readerI("アイテム数")) Then
                                Dim ランク As String = readerI("GPLIランク").ToString()
                                Dim 数 As Double = Convert.ToDouble(readerI("アイテム数"))
                                Select Case ランク
                                    Case "GPLI_A1" : I1 = 数
                                    Case "GPLI_A2" : I2 = 数
                                    Case "GPLI_B" : I3 = 数
                                    Case "GPLI_C" : I4 = 数
                                    Case "GPLI_D" : I5 = 数
                                End Select
                            End If
                        End While
                    End Using
                End Using

                Try
                    Using cmdDropI As New OleDbCommand("DROP TABLE T590_Iアイテム一時", conn)
                        cmdDropI.ExecuteNonQuery()
                    End Using
                Catch
                End Try

                ' === バラ数量 ===
                Dim バラ数量 As Double = 0
                Using cmdBara As New OleDbCommand("SELECT SUM(Gバ) FROM T800_計算対象", conn)
                    Dim result = cmdBara.ExecuteScalar()
                    If Not IsDBNull(result) AndAlso result IsNot Nothing Then
                        バラ数量 = Convert.ToDouble(result)
                    End If
                End Using

                ' === 行数 ===
                Dim 行数 As Double = 0
                Using cmdCount As New OleDbCommand("SELECT COUNT(*) FROM T800_計算対象", conn)
                    Dim result = cmdCount.ExecuteScalar()
                    If result IsNot Nothing AndAlso Not IsDBNull(result) Then
                        Double.TryParse(result.ToString(), 行数)
                    End If
                End Using

                ' === 行数01-25 ===
                Dim 行数Arr(25) As Double
                Using cmdRows As New OleDbCommand("SELECT GPLEランク, GPLIランク, COUNT(*) AS 行数 FROM T800_計算対象 GROUP BY GPLEランク, GPLIランク", conn)
                    Using reader As OleDbDataReader = cmdRows.ExecuteReader()
                        While reader.Read()
                            If Not IsDBNull(reader("GPLEランク")) AndAlso Not IsDBNull(reader("GPLIランク")) AndAlso Not IsDBNull(reader("行数")) Then
                                Dim GPLEランク As String = reader("GPLEランク").ToString()
                                Dim GPLIランク As String = reader("GPLIランク").ToString()
                                Dim 数 As Double = Convert.ToDouble(reader("行数"))
                                Dim キー As String = GPLEランク & "_" & GPLIランク
                                Select Case キー
                                    Case "GPLE_A1_GPLI_A1" : 行数Arr(1) = 数
                                    Case "GPLE_A1_GPLI_A2" : 行数Arr(2) = 数
                                    Case "GPLE_A1_GPLI_B" : 行数Arr(3) = 数
                                    Case "GPLE_A1_GPLI_C" : 行数Arr(4) = 数
                                    Case "GPLE_A1_GPLI_D" : 行数Arr(5) = 数
                                    Case "GPLE_A2_GPLI_A1" : 行数Arr(6) = 数
                                    Case "GPLE_A2_GPLI_A2" : 行数Arr(7) = 数
                                    Case "GPLE_A2_GPLI_B" : 行数Arr(8) = 数
                                    Case "GPLE_A2_GPLI_C" : 行数Arr(9) = 数
                                    Case "GPLE_A2_GPLI_D" : 行数Arr(10) = 数
                                    Case "GPLE_B_GPLI_A1" : 行数Arr(11) = 数
                                    Case "GPLE_B_GPLI_A2" : 行数Arr(12) = 数
                                    Case "GPLE_B_GPLI_B" : 行数Arr(13) = 数
                                    Case "GPLE_B_GPLI_C" : 行数Arr(14) = 数
                                    Case "GPLE_B_GPLI_D" : 行数Arr(15) = 数
                                    Case "GPLE_C_GPLI_A1" : 行数Arr(16) = 数
                                    Case "GPLE_C_GPLI_A2" : 行数Arr(17) = 数
                                    Case "GPLE_C_GPLI_B" : 行数Arr(18) = 数
                                    Case "GPLE_C_GPLI_C" : 行数Arr(19) = 数
                                    Case "GPLE_C_GPLI_D" : 行数Arr(20) = 数
                                    Case "GPLE_D_GPLI_A1" : 行数Arr(21) = 数
                                    Case "GPLE_D_GPLI_A2" : 行数Arr(22) = 数
                                    Case "GPLE_D_GPLI_B" : 行数Arr(23) = 数
                                    Case "GPLE_D_GPLI_C" : 行数Arr(24) = 数
                                    Case "GPLE_D_GPLI_D" : 行数Arr(25) = 数
                                End Select
                            End If
                        End While
                    End Using
                End Using

                ' === バラ数01-25 (Gバ sum) ===
                Dim バラArr(25) As Double
                Using cmdB As New OleDbCommand("SELECT GPLEランク, GPLIランク, Sum(Gバ) AS Gバの合計 FROM T800_計算対象 GROUP BY GPLEランク, GPLIランク", conn)
                    Using readerB As OleDbDataReader = cmdB.ExecuteReader()
                        While readerB.Read()
                            If Not IsDBNull(readerB("GPLEランク")) AndAlso Not IsDBNull(readerB("GPLIランク")) AndAlso Not IsDBNull(readerB("Gバの合計")) Then
                                Dim GPLEランク As String = readerB("GPLEランク").ToString()
                                Dim GPLIランク As String = readerB("GPLIランク").ToString()
                                Dim 数 As Double = Convert.ToDouble(readerB("Gバの合計"))
                                Dim キー As String = GPLEランク & "_" & GPLIランク
                                Select Case キー
                                    Case "GPLE_A1_GPLI_A1" : バラArr(1) = 数
                                    Case "GPLE_A1_GPLI_A2" : バラArr(2) = 数
                                    Case "GPLE_A1_GPLI_B" : バラArr(3) = 数
                                    Case "GPLE_A1_GPLI_C" : バラArr(4) = 数
                                    Case "GPLE_A1_GPLI_D" : バラArr(5) = 数
                                    Case "GPLE_A2_GPLI_A1" : バラArr(6) = 数
                                    Case "GPLE_A2_GPLI_A2" : バラArr(7) = 数
                                    Case "GPLE_A2_GPLI_B" : バラArr(8) = 数
                                    Case "GPLE_A2_GPLI_C" : バラArr(9) = 数
                                    Case "GPLE_A2_GPLI_D" : バラArr(10) = 数
                                    Case "GPLE_B_GPLI_A1" : バラArr(11) = 数
                                    Case "GPLE_B_GPLI_A2" : バラArr(12) = 数
                                    Case "GPLE_B_GPLI_B" : バラArr(13) = 数
                                    Case "GPLE_B_GPLI_C" : バラArr(14) = 数
                                    Case "GPLE_B_GPLI_D" : バラArr(15) = 数
                                    Case "GPLE_C_GPLI_A1" : バラArr(16) = 数
                                    Case "GPLE_C_GPLI_A2" : バラArr(17) = 数
                                    Case "GPLE_C_GPLI_B" : バラArr(18) = 数
                                    Case "GPLE_C_GPLI_C" : バラArr(19) = 数
                                    Case "GPLE_C_GPLI_D" : バラArr(20) = 数
                                    Case "GPLE_D_GPLI_A1" : バラArr(21) = 数
                                    Case "GPLE_D_GPLI_A2" : バラArr(22) = 数
                                    Case "GPLE_D_GPLI_B" : バラArr(23) = 数
                                    Case "GPLE_D_GPLI_C" : バラArr(24) = 数
                                    Case "GPLE_D_GPLI_D" : バラArr(25) = 数
                                End Select
                            End If
                        End While
                    End Using
                End Using

                ' === ケース (Gケ) ===
                Dim ケースArr(25) As Double
                Using cmdK As New OleDbCommand("SELECT GPLEランク, GPLIランク, Sum(Gケ) AS Gケの合計 FROM T800_計算対象 GROUP BY GPLEランク, GPLIランク", conn)
                    Using readerK As OleDbDataReader = cmdK.ExecuteReader()
                        While readerK.Read()
                            If Not IsDBNull(readerK("GPLEランク")) AndAlso Not IsDBNull(readerK("GPLIランク")) AndAlso Not IsDBNull(readerK("Gケの合計")) Then
                                Dim GPLEランク As String = readerK("GPLEランク").ToString()
                                Dim GPLIランク As String = readerK("GPLIランク").ToString()
                                Dim 数 As Double = Convert.ToDouble(readerK("Gケの合計"))
                                Dim キー As String = GPLEランク & "_" & GPLIランク
                                Select Case キー
                                    Case "GPLE_A1_GPLI_A1" : ケースArr(1) = 数
                                    Case "GPLE_A1_GPLI_A2" : ケースArr(2) = 数
                                    Case "GPLE_A1_GPLI_B" : ケースArr(3) = 数
                                    Case "GPLE_A1_GPLI_C" : ケースArr(4) = 数
                                    Case "GPLE_A1_GPLI_D" : ケースArr(5) = 数
                                    Case "GPLE_A2_GPLI_A1" : ケースArr(6) = 数
                                    Case "GPLE_A2_GPLI_A2" : ケースArr(7) = 数
                                    Case "GPLE_A2_GPLI_B" : ケースArr(8) = 数
                                    Case "GPLE_A2_GPLI_C" : ケースArr(9) = 数
                                    Case "GPLE_A2_GPLI_D" : ケースArr(10) = 数
                                    Case "GPLE_B_GPLI_A1" : ケースArr(11) = 数
                                    Case "GPLE_B_GPLI_A2" : ケースArr(12) = 数
                                    Case "GPLE_B_GPLI_B" : ケースArr(13) = 数
                                    Case "GPLE_B_GPLI_C" : ケースArr(14) = 数
                                    Case "GPLE_B_GPLI_D" : ケースArr(15) = 数
                                    Case "GPLE_C_GPLI_A1" : ケースArr(16) = 数
                                    Case "GPLE_C_GPLI_A2" : ケースArr(17) = 数
                                    Case "GPLE_C_GPLI_B" : ケースArr(18) = 数
                                    Case "GPLE_C_GPLI_C" : ケースArr(19) = 数
                                    Case "GPLE_C_GPLI_D" : ケースArr(20) = 数
                                    Case "GPLE_D_GPLI_A1" : ケースArr(21) = 数
                                    Case "GPLE_D_GPLI_A2" : ケースArr(22) = 数
                                    Case "GPLE_D_GPLI_B" : ケースArr(23) = 数
                                    Case "GPLE_D_GPLI_C" : ケースArr(24) = 数
                                    Case "GPLE_D_GPLI_D" : ケースArr(25) = 数
                                End Select
                            End If
                        End While
                    End Using
                End Using

                ' === PL (GPL) ===
                Dim PLArr(25) As Double
                Using cmdPL As New OleDbCommand("SELECT GPLEランク, GPLIランク, Sum(GPL) AS GPLの合計 FROM T800_計算対象 GROUP BY GPLEランク, GPLIランク", conn)
                    Using readerPL As OleDbDataReader = cmdPL.ExecuteReader()
                        While readerPL.Read()
                            If Not IsDBNull(readerPL("GPLEランク")) AndAlso Not IsDBNull(readerPL("GPLIランク")) AndAlso Not IsDBNull(readerPL("GPLの合計")) Then
                                Dim GPLEランク As String = readerPL("GPLEランク").ToString()
                                Dim GPLIランク As String = readerPL("GPLIランク").ToString()
                                Dim 数 As Double = Convert.ToDouble(readerPL("GPLの合計"))
                                Dim キー As String = GPLEランク & "_" & GPLIランク
                                Select Case キー
                                    Case "GPLE_A1_GPLI_A1" : PLArr(1) = 数
                                    Case "GPLE_A1_GPLI_A2" : PLArr(2) = 数
                                    Case "GPLE_A1_GPLI_B" : PLArr(3) = 数
                                    Case "GPLE_A1_GPLI_C" : PLArr(4) = 数
                                    Case "GPLE_A1_GPLI_D" : PLArr(5) = 数
                                    Case "GPLE_A2_GPLI_A1" : PLArr(6) = 数
                                    Case "GPLE_A2_GPLI_A2" : PLArr(7) = 数
                                    Case "GPLE_A2_GPLI_B" : PLArr(8) = 数
                                    Case "GPLE_A2_GPLI_C" : PLArr(9) = 数
                                    Case "GPLE_A2_GPLI_D" : PLArr(10) = 数
                                    Case "GPLE_B_GPLI_A1" : PLArr(11) = 数
                                    Case "GPLE_B_GPLI_A2" : PLArr(12) = 数
                                    Case "GPLE_B_GPLI_B" : PLArr(13) = 数
                                    Case "GPLE_B_GPLI_C" : PLArr(14) = 数
                                    Case "GPLE_B_GPLI_D" : PLArr(15) = 数
                                    Case "GPLE_C_GPLI_A1" : PLArr(16) = 数
                                    Case "GPLE_C_GPLI_A2" : PLArr(17) = 数
                                    Case "GPLE_C_GPLI_B" : PLArr(18) = 数
                                    Case "GPLE_C_GPLI_C" : PLArr(19) = 数
                                    Case "GPLE_C_GPLI_D" : PLArr(20) = 数
                                    Case "GPLE_D_GPLI_A1" : PLArr(21) = 数
                                    Case "GPLE_D_GPLI_A2" : PLArr(22) = 数
                                    Case "GPLE_D_GPLI_B" : PLArr(23) = 数
                                    Case "GPLE_D_GPLI_C" : PLArr(24) = 数
                                    Case "GPLE_D_GPLI_D" : PLArr(25) = 数
                                End Select
                            End If
                        End While
                    End Using
                End Using

                ' === 容積 (G容) ===
                Dim 容積Arr(25) As Double
                Using cmd容積 As New OleDbCommand("SELECT GPLEランク, GPLIランク, Sum(G容) AS G容の合計 FROM T800_計算対象 GROUP BY GPLEランク, GPLIランク", conn)
                    Using reader容積 As OleDbDataReader = cmd容積.ExecuteReader()
                        While reader容積.Read()
                            If Not IsDBNull(reader容積("GPLEランク")) AndAlso Not IsDBNull(reader容積("GPLIランク")) AndAlso Not IsDBNull(reader容積("G容の合計")) Then
                                Dim GPLEランク As String = reader容積("GPLEランク").ToString()
                                Dim GPLIランク As String = reader容積("GPLIランク").ToString()
                                Dim 数 As Double = Convert.ToDouble(reader容積("G容の合計"))
                                Dim キー As String = GPLEランク & "_" & GPLIランク
                                Select Case キー
                                    Case "GPLE_A1_GPLI_A1" : 容積Arr(1) = 数
                                    Case "GPLE_A1_GPLI_A2" : 容積Arr(2) = 数
                                    Case "GPLE_A1_GPLI_B" : 容積Arr(3) = 数
                                    Case "GPLE_A1_GPLI_C" : 容積Arr(4) = 数
                                    Case "GPLE_A1_GPLI_D" : 容積Arr(5) = 数
                                    Case "GPLE_A2_GPLI_A1" : 容積Arr(6) = 数
                                    Case "GPLE_A2_GPLI_A2" : 容積Arr(7) = 数
                                    Case "GPLE_A2_GPLI_B" : 容積Arr(8) = 数
                                    Case "GPLE_A2_GPLI_C" : 容積Arr(9) = 数
                                    Case "GPLE_A2_GPLI_D" : 容積Arr(10) = 数
                                    Case "GPLE_B_GPLI_A1" : 容積Arr(11) = 数
                                    Case "GPLE_B_GPLI_A2" : 容積Arr(12) = 数
                                    Case "GPLE_B_GPLI_B" : 容積Arr(13) = 数
                                    Case "GPLE_B_GPLI_C" : 容積Arr(14) = 数
                                    Case "GPLE_B_GPLI_D" : 容積Arr(15) = 数
                                    Case "GPLE_C_GPLI_A1" : 容積Arr(16) = 数
                                    Case "GPLE_C_GPLI_A2" : 容積Arr(17) = 数
                                    Case "GPLE_C_GPLI_B" : 容積Arr(18) = 数
                                    Case "GPLE_C_GPLI_C" : 容積Arr(19) = 数
                                    Case "GPLE_C_GPLI_D" : 容積Arr(20) = 数
                                    Case "GPLE_D_GPLI_A1" : 容積Arr(21) = 数
                                    Case "GPLE_D_GPLI_A2" : 容積Arr(22) = 数
                                    Case "GPLE_D_GPLI_B" : 容積Arr(23) = 数
                                    Case "GPLE_D_GPLI_C" : 容積Arr(24) = 数
                                    Case "GPLE_D_GPLI_D" : 容積Arr(25) = 数
                                End Select
                            End If
                        End While
                    End Using
                End Using

                ' === 重量 (G重) ===
                Dim 重量Arr(25) As Double
                Using cmd重 As New OleDbCommand("SELECT GPLEランク, GPLIランク, Sum(G重) AS G重の合計 FROM T800_計算対象 GROUP BY GPLEランク, GPLIランク", conn)
                    Using reader重 As OleDbDataReader = cmd重.ExecuteReader()
                        While reader重.Read()
                            If Not IsDBNull(reader重("GPLEランク")) AndAlso Not IsDBNull(reader重("GPLIランク")) AndAlso Not IsDBNull(reader重("G重の合計")) Then
                                Dim GPLEランク As String = reader重("GPLEランク").ToString()
                                Dim GPLIランク As String = reader重("GPLIランク").ToString()
                                Dim 数 As Double = Convert.ToDouble(reader重("G重の合計"))
                                Dim キー As String = GPLEランク & "_" & GPLIランク
                                Select Case キー
                                    Case "GPLE_A1_GPLI_A1" : 重量Arr(1) = 数
                                    Case "GPLE_A1_GPLI_A2" : 重量Arr(2) = 数
                                    Case "GPLE_A1_GPLI_B" : 重量Arr(3) = 数
                                    Case "GPLE_A1_GPLI_C" : 重量Arr(4) = 数
                                    Case "GPLE_A1_GPLI_D" : 重量Arr(5) = 数
                                    Case "GPLE_A2_GPLI_A1" : 重量Arr(6) = 数
                                    Case "GPLE_A2_GPLI_A2" : 重量Arr(7) = 数
                                    Case "GPLE_A2_GPLI_B" : 重量Arr(8) = 数
                                    Case "GPLE_A2_GPLI_C" : 重量Arr(9) = 数
                                    Case "GPLE_A2_GPLI_D" : 重量Arr(10) = 数
                                    Case "GPLE_B_GPLI_A1" : 重量Arr(11) = 数
                                    Case "GPLE_B_GPLI_A2" : 重量Arr(12) = 数
                                    Case "GPLE_B_GPLI_B" : 重量Arr(13) = 数
                                    Case "GPLE_B_GPLI_C" : 重量Arr(14) = 数
                                    Case "GPLE_B_GPLI_D" : 重量Arr(15) = 数
                                    Case "GPLE_C_GPLI_A1" : 重量Arr(16) = 数
                                    Case "GPLE_C_GPLI_A2" : 重量Arr(17) = 数
                                    Case "GPLE_C_GPLI_B" : 重量Arr(18) = 数
                                    Case "GPLE_C_GPLI_C" : 重量Arr(19) = 数
                                    Case "GPLE_C_GPLI_D" : 重量Arr(20) = 数
                                    Case "GPLE_D_GPLI_A1" : 重量Arr(21) = 数
                                    Case "GPLE_D_GPLI_A2" : 重量Arr(22) = 数
                                    Case "GPLE_D_GPLI_B" : 重量Arr(23) = 数
                                    Case "GPLE_D_GPLI_C" : 重量Arr(24) = 数
                                    Case "GPLE_D_GPLI_D" : 重量Arr(25) = 数
                                End Select
                            End If
                        End While
                    End Using
                End Using

                ' === T600 テーブル作成（既存を削除して単一行を作成） ===
                Try
                    Using cmdDrop As New OleDbCommand("DROP TABLE T600_手計算", conn)
                        cmdDrop.ExecuteNonQuery()
                    End Using
                Catch
                End Try

                ' Build CREATE TABLE statement
                Dim cols As New List(Of String)()
                cols.Add("モデル連番 LONG")
                cols.Add("出荷先数 DOUBLE")
                cols.Add("アイテム数 DOUBLE")
                cols.Add("バラ数量 DOUBLE")
                cols.Add("行数 DOUBLE")
                For i As Integer = 1 To 5
                    cols.Add($"E{i} DOUBLE")
                Next
                For i As Integer = 1 To 5
                    cols.Add($"I{i} DOUBLE")
                Next
                For i As Integer = 1 To 25
                    cols.Add($"行数{i:D2} DOUBLE")
                Next
                For i As Integer = 1 To 25
                    cols.Add($"バラ数{i:D2} DOUBLE")
                Next
                For i As Integer = 1 To 25
                    cols.Add($"ケース{i:D2} DOUBLE")
                Next
                For i As Integer = 1 To 25
                    cols.Add($"PL{i:D2} DOUBLE")
                Next
                For i As Integer = 1 To 25
                    cols.Add($"容積{i:D2} DOUBLE")
                Next
                For i As Integer = 1 To 25
                    cols.Add($"重量{i:D2} DOUBLE")
                Next

                Dim createSql As String = $"CREATE TABLE T600_手計算 (" & String.Join(",", cols) & ")"
                Using cmdCreate As New OleDbCommand(createSql, conn)
                    cmdCreate.ExecuteNonQuery()
                End Using

                ' Insert single row (モデル連番 = 1)
                Dim colNames As New List(Of String)()
                Dim paramNames As New List(Of String)()
                Dim paramValues As New List(Of Object)()

                colNames.Add("モデル連番") : paramNames.Add("@p0") : paramValues.Add(1)
                colNames.Add("出荷先数") : paramNames.Add("@p1") : paramValues.Add(E1 + E2 + E3 + E4 + E5)
                colNames.Add("アイテム数") : paramNames.Add("@p2") : paramValues.Add(I1 + I2 + I3 + I4 + I5)
                colNames.Add("バラ数量") : paramNames.Add("@p3") : paramValues.Add(バラ数量)
                colNames.Add("行数") : paramNames.Add("@p4") : paramValues.Add(行数)

                ' E1..E5
                For i As Integer = 1 To 5
                    colNames.Add($"E{i}") : paramNames.Add($"@p{colNames.Count}")
                Next
                ' I1..I5
                For i As Integer = 1 To 5
                    colNames.Add($"I{i}") : paramNames.Add($"@p{colNames.Count}")
                Next
                ' 行数01-25
                For i As Integer = 1 To 25
                    colNames.Add($"行数{i:D2}") : paramNames.Add($"@p{colNames.Count}")
                Next
                ' バラ数01-25
                For i As Integer = 1 To 25
                    colNames.Add($"バラ数{i:D2}") : paramNames.Add($"@p{colNames.Count}")
                Next
                ' ケース01-25
                For i As Integer = 1 To 25
                    colNames.Add($"ケース{i:D2}") : paramNames.Add($"@p{colNames.Count}")
                Next
                ' PL01-25
                For i As Integer = 1 To 25
                    colNames.Add($"PL{i:D2}") : paramNames.Add($"@p{colNames.Count}")
                Next
                ' 容積01-25
                For i As Integer = 1 To 25
                    colNames.Add($"容積{i:D2}") : paramNames.Add($"@p{colNames.Count}")
                Next
                ' 重量01-25
                For i As Integer = 1 To 25
                    colNames.Add($"重量{i:D2}") : paramNames.Add($"@p{colNames.Count}")
                Next

                ' Build values list for parameters (only the 6 groups of 25: 行数, バラ数, ケース, PL, 容積, 重量)
                Dim values As New List(Of Object)()
                ' 行数01..25
                For i As Integer = 1 To 25
                    values.Add(行数Arr(i))
                Next
                ' バラ数01..25
                For i As Integer = 1 To 25
                    values.Add(バラArr(i))
                Next
                ' ケース
                For i As Integer = 1 To 25
                    values.Add(ケースArr(i))
                Next
                ' PL
                For i As Integer = 1 To 25
                    values.Add(PLArr(i))
                Next
                ' 容積
                For i As Integer = 1 To 25
                    values.Add(容積Arr(i))
                Next
                ' 重量
                For i As Integer = 1 To 25
                    values.Add(重量Arr(i))
                Next

                ' Now assemble parameterized INSERT (note: paramValues list must align)
                ' We've already added model連番 and summary fields earlier, need to combine
                ' Build final parameter list in order of colNames
                Dim finalParams As New List(Of Object)()
                ' First two summary numbers were already added to paramValues earlier as p1..p4; to simplify, rebuild sequentially:
                finalParams.Add(1) ' モデル連番
                finalParams.Add(E1 + E2 + E3 + E4 + E5) ' 出荷先数
                finalParams.Add(I1 + I2 + I3 + I4 + I5) ' アイテム数
                finalParams.Add(バラ数量)
                finalParams.Add(行数)
                ' E1..E5
                finalParams.AddRange(New Object() {E1, E2, E3, E4, E5})
                ' I1..I5
                finalParams.AddRange(New Object() {I1, I2, I3, I4, I5})
                ' 行数01..25
                finalParams.AddRange(values.GetRange(0, 25))
                ' バラ数01..25
                finalParams.AddRange(values.GetRange(25, 25))
                ' ケース
                finalParams.AddRange(values.GetRange(50, 25))
                ' PL
                finalParams.AddRange(values.GetRange(75, 25))
                ' 容積
                finalParams.AddRange(values.GetRange(100, 25))
                ' 重量
                finalParams.AddRange(values.GetRange(125, 25))

                ' Build parameter placeholders
                Dim placeholders As New List(Of String)()
                For i As Integer = 0 To finalParams.Count - 1
                    placeholders.Add("@v" & i.ToString())
                Next

                Dim insertSql As String = $"INSERT INTO T600_手計算 (" & String.Join(",", colNames) & ") VALUES (" & String.Join(",", placeholders) & ")"
                Using cmdIns As New OleDbCommand(insertSql, conn)
                    For i As Integer = 0 To finalParams.Count - 1
                        cmdIns.Parameters.AddWithValue(placeholders(i), finalParams(i))
                    Next
                    cmdIns.ExecuteNonQuery()
                End Using

            Catch ex As Exception
                MsgBox($"T600_手計算作成エラー: {ex.Message}", MsgBoxStyle.Critical, "エラー")
            End Try
        End Using
    End Sub

    Private Sub 結果エクスポートBt_Click(sender As Object, e As EventArgs) Handles 結果エクスポートBt.Click
        結果エクスポート()
    End Sub
    ' 表示チェック2Gbのラジオボタン変更時に再描画
    Private Sub 表示チェック2Gb_Changed(sender As Object, e As EventArgs) Handles 抽出_出庫回数2Rb.CheckedChanged, 抽出_バラ2Rb.CheckedChanged, 抽出_ケース2Rb.CheckedChanged, 抽出_PL2Rb.CheckedChanged, 抽出_容積2Rb.CheckedChanged, 抽出_重量2Rb.CheckedChanged
        予測計算Gb作成()
        誤差表示()

        ' 実計算表示も同期して更新（T600 があれば反映される）
        Try
            Update実計算表示FromT600()
        Catch
            ' 更新できなくても無視
        End Try
    End Sub
    Private Sub 閉じるBt_Click(sender As Object, e As EventArgs) Handles 閉じるBt.Click
        Me.Close()
    End Sub
#End Region


    ' ========================================
    ' 出力フィールド名リストの初期化
    ' ========================================
    Private Sub 出力フィールド名リスト初期化()
        出力フィールド名リスト = New List(Of String) From {
            "バラ数量", "行数",
            "E1", "E2", "E3", "E4", "E5",
            "I1", "I2", "I3", "I4", "I5"
        }

        ' 行数01-25を追加
        For i As Integer = 1 To 25
            出力フィールド名リスト.Add($"行数{i:D2}")
        Next

        ' バラ数01-25を追加
        For i As Integer = 1 To 25
            出力フィールド名リスト.Add($"バラ数{i:D2}")
        Next

        ' ケース01-25を追加
        For i As Integer = 1 To 25
            出力フィールド名リスト.Add($"ケース{i:D2}")
        Next

        ' PL01-25を追加
        For i As Integer = 1 To 25
            出力フィールド名リスト.Add($"PL{i:D2}")
        Next

        ' 容積01-25を追加
        For i As Integer = 1 To 25
            出力フィールド名リスト.Add($"容積{i:D2}")
        Next

        ' 重量01-25を追加
        For i As Integer = 1 To 25
            出力フィールド名リスト.Add($"重量{i:D2}")
        Next
    End Sub

    ' ========================================
    ' 学習済モデル読込
    ' ========================================
    Private Sub 学習済モデル読込()
        modelFolder = Dat保存Path & "\Models"
        Try
            trainedModels.Clear()
            predictionEngines.Clear()

            If Not Directory.Exists(modelFolder) Then
                モデル状態Lb.Text = "モデル: フォルダが見つかりません"
                モデル状態Lb.ForeColor = Color.Red
                Exit Sub
            End If

            ' モデルフォルダ内のすべての.zipファイルを読み込む
            Dim modelFiles = Directory.GetFiles(modelFolder, "*.zip")

            If modelFiles.Length = 0 Then
                モデル状態Lb.Text = "モデル: ファイルが見つかりません"
                モデル状態Lb.ForeColor = Color.Red
                Exit Sub
            End If

            For Each modelPath As String In modelFiles
                Try
                    Dim model = mlContext.Model.Load(modelPath, Nothing)
                    Dim fieldName As String = Path.GetFileNameWithoutExtension(modelPath)
                    trainedModels(fieldName) = model
                Catch ex As Exception
                    ' 読み込めないモデルはスキップ
                    Continue For
                End Try
            Next

            モデル状態Lb.Text = $"モデル: {trainedModels.Count} 個読込済"
            モデル状態Lb.ForeColor = Color.Green

        Catch ex As Exception
            モデル状態Lb.Text = "モデル: 読込エラー"
            モデル状態Lb.ForeColor = Color.Red
            MsgBox($"モデル読込エラー: {ex.Message}", MsgBoxStyle.Critical, "エラー")
        End Try
    End Sub

    ' ========================================
    ' 予測実行
    ' ========================================
    Private Async Function 予測実行() As Task

        Try
            ' モデルが読み込まれているか確認
            If trainedModels Is Nothing OrElse trainedModels.Count = 0 Then
                MsgBox("モデルが読み込まれていません。" & vbCrLf &
                      "学習済モデルを配置してください。" & vbCrLf &
                      "モデルフォルダ: " & modelFolder,
                      MsgBoxStyle.Exclamation, "モデル未読込")
                Return
            End If

            ' 入力値の検証
            Dim 出荷先数値 As Single
            Dim アイテム数値 As Single

            If Not Single.TryParse(出荷先数Tb.Text, 出荷先数値) Then
                MsgBox("出荷先数に有効な数値を入力してください。" & vbCrLf &
                      "例: 100, 2500",
                      MsgBoxStyle.Exclamation, "入力エラー")
                出荷先数Tb.Focus()
                Return
            End If

            If Not Single.TryParse(アイテム数Tb.Text, アイテム数値) Then
                MsgBox("アイテム数に有効な数値を入力してください。" & vbCrLf &
                      "例: 10, 50",
                      MsgBoxStyle.Exclamation, "入力エラー")
                アイテム数Tb.Focus()
                Return
            End If

            ' 入力データを作成
            Dim input As New InputData With {
                .出荷先数 = 出荷先数値,
                .アイテム数 = アイテム数値
            }

            ' 予測状態表示
            予測状態Lb.Text = "予測: 実行中..."
            予測状態Lb.ForeColor = Color.Orange
            
            ' 重い予測処理をバックグラウンドで実行
            Dim predictions As Dictionary(Of String, Single) = Await Task.Run(
                Function()
                    Dim results As New Dictionary(Of String, Single)
                    For Each kvp As KeyValuePair(Of String, ITransformer) In trainedModels
                        Try
                            Dim fieldName As String = kvp.Key
                            Dim model = kvp.Value

                            ' 予測エンジンをキャッシュから取得、なければ作成
                            Dim pe As PredictionEngine(Of InputData, SinglePrediction)
                            SyncLock predictionEngines
                                If Not predictionEngines.ContainsKey(fieldName) Then
                                    predictionEngines(fieldName) = mlContext.Model.CreatePredictionEngine(Of InputData, SinglePrediction)(model)
                                End If
                                pe = predictionEngines(fieldName)
                            End SyncLock

                            ' 予測を実行
                            Dim prediction = pe.Predict(input)
                            results(fieldName) = prediction.Score

                        Catch ex As Exception
                            Continue For
                        End Try
                    Next
                    Return results
                End Function)

            ' 入力値に近い場合は予測値を入力値で補正する（例: アイテム数がほぼ一致するなら入力値を優先）
            Dim 入力アイテム As Single
            If Single.TryParse(アイテム数Tb.Text, 入力アイテム) Then
                If predictions.ContainsKey("アイテム数") Then
                    ' 許容差を定数で管理
                    If Math.Abs(predictions("アイテム数") - 入力アイテム) <= ITEM_TOLERANCE Then
                        predictions("アイテム数") = 入力アイテム
                    End If
                End If
            End If

            ' 結果を表示
            予測結果表示(predictions)

            予測状態Lb.Text = $"予測: 完了 ({predictions.Count} 項目)"
            予測状態Lb.ForeColor = Color.Green

            ' MsgBox($"予測が完了しました。{vbCrLf}{predictions.Count} 項目の予測値を計算しました。", MsgBoxStyle.Information, "予測完了")

        Catch ex As Exception
            予測状態Lb.Text = "予測: エラー"
            予測状態Lb.ForeColor = Color.Red
            MsgBox($"予測エラー: {ex.Message}", MsgBoxStyle.Critical, "エラー")
        End Try
    End Function

    ' ========================================
    ' 予測結果を表示
    ' ========================================
    Private Sub 予測結果表示(predictions As Dictionary(Of String, Single))
        ' DataGridViewに結果を表示
        Dim dt As New DataTable()
        dt.Columns.Add("項目", GetType(String))
        dt.Columns.Add("予測値", GetType(String))

        ' 出力フィールド名リストの順序に従って予測結果を表示
        For Each fieldName As String In 出力フィールド名リスト
            If predictions.ContainsKey(fieldName) Then
                dt.Rows.Add(fieldName, predictions(fieldName).ToString("N2"))
            End If
        Next

        ' リストにないフィールドも追加（念のため）
        For Each kvp As KeyValuePair(Of String, Single) In predictions
            If Not 出力フィールド名リスト.Contains(kvp.Key) Then
                dt.Rows.Add(kvp.Key, kvp.Value.ToString("N2"))
            End If
        Next

        AI予測データDgv.DataSource = dt
        AI予測データDgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells
        AI予測データDgv.Visible = True

    End Sub

    ' ========================================
    ' 結果をCSVにエクスポート
    ' ========================================
    Private Sub 結果エクスポート()
        Try
            If AI予測データDgv.DataSource Is Nothing Then
                MsgBox("エクスポートする予測結果がありません。",
                      MsgBoxStyle.Exclamation, "データなし")
                Exit Sub
            End If

            ' ファイル名を生成（入力条件を含めない簡潔版）
            Dim fileName As String = $"予測結果_{DateTime.Now:yyyyMMdd_HHmmss}.csv"
            Dim exportPath As String = Path.Combine(Dat保存Path, fileName)

            ' フォルダが存在しない場合は作成
            Dim exportFolder As String = Path.GetDirectoryName(exportPath)
            If Not Directory.Exists(exportFolder) Then
                Directory.CreateDirectory(exportFolder)
            End If

            ' DataGridViewの内容をCSVに出力（入力条件も含める）
            Dim dt = CType(AI予測データDgv.DataSource, DataTable)

            Using writer As New StreamWriter(exportPath, False, System.Text.Encoding.UTF8)
                ' 入力条件をヘッダーとして出力
                writer.WriteLine($"# 出荷先数,{出荷先数Tb.Text}")
                writer.WriteLine($"# アイテム数,{アイテム数Tb.Text}")
                writer.WriteLine("# ---,")

                ' ヘッダー行（DataGridViewの列名と同じ）
                writer.WriteLine("項目,予測値")

                ' データ行（DataGridViewの内容と同じ）
                For Each row As DataRow In dt.Rows
                    writer.WriteLine($"{row(0)},{row(1)}")
                Next
            End Using

            MsgBox($"予測結果をエクスポートしました。{vbCrLf}保存先: {exportPath}",
                  MsgBoxStyle.Information, "エクスポート完了")

        Catch ex As Exception
            MsgBox($"エクスポートエラー: {ex.Message}", MsgBoxStyle.Critical, "エラー")
        End Try
    End Sub



＃Region "AI予測EIQマトリクス表作成"
    Private Sub AI予測EIQマトリクス表（)
        ' nIndex is not used in this method; previous assignments removed

        Try
            ' 予測結果が存在するか確認
            If AI予測データDgv Is Nothing OrElse AI予測データDgv.Rows Is Nothing OrElse AI予測データDgv.Rows.Count = 0 Then
                ' クリア
                抽出I_A1Tb.Text = String.Empty
                抽出I_A2Tb.Text = String.Empty
                抽出I_BTb.Text = String.Empty
                抽出I_CTb.Text = String.Empty
                抽出I_DTb.Text = String.Empty
                抽出I_計Tb.Text = String.Empty
                Return
            End If

            ' 予測結果を辞書にキャッシュ（キー: 項目名, 値: 四捨五入した整数文字列）
            Dim pred As New Dictionary(Of String, String)(StringComparer.OrdinalIgnoreCase)
            For Each r As DataGridViewRow In AI予測データDgv.Rows
                If r.IsNewRow Then Continue For
                Dim key = If(r.Cells(0).Value, String.Empty).ToString()
                Dim raw = If(r.Cells(1).Value, String.Empty).ToString()
                If String.IsNullOrEmpty(key) Then Continue For
                pred(key) = ToRoundedString(raw)
            Next

            ' 表示単位プレフィックスを決定
            Dim prefix As String = ""
            If 抽出_出庫回数Rb.Checked = True Then prefix = "行数"
            If 抽出_バラRb.Checked = True Then prefix = "バラ数"
            If 抽出_ケースRb.Checked = True Then prefix = "ケース"
            If 抽出_PLRb.Checked = True Then prefix = "PL"
            If 抽出_容積Rb.Checked = True Then prefix = "容積"
            If 抽出_重量Rb.Checked = True Then prefix = "重量"

            ' アイテム数(I1..I5) を抽出して入力欄へ
            抽出I_A1Tb.Text = If(pred.ContainsKey("I1"), pred("I1"), "")
            抽出I_A2Tb.Text = If(pred.ContainsKey("I2"), pred("I2"), "")
            抽出I_BTb.Text = If(pred.ContainsKey("I3"), pred("I3"), "")
            抽出I_CTb.Text = If(pred.ContainsKey("I4"), pred("I4"), "")
            抽出I_DTb.Text = If(pred.ContainsKey("I5"), pred("I5"), "")

            ' 出荷先数(E1..E5) を抽出して入力欄へ
            抽出E_A1Tb.Text = If(pred.ContainsKey("E1"), pred("E1"), "")
            抽出E_A2Tb.Text = If(pred.ContainsKey("E2"), pred("E2"), "")
            抽出E_BTb.Text = If(pred.ContainsKey("E3"), pred("E3"), "")
            抽出E_CTb.Text = If(pred.ContainsKey("E4"), pred("E4"), "")
            抽出E_DTb.Text = If(pred.ContainsKey("E5"), pred("E5"), "")

            ' 出荷先数/アイテム数 を抽出して入力欄へ
            出荷先数Tb.Text = If(pred.ContainsKey("出荷先数"), pred("出荷先数"), If(pred.ContainsKey("発送先数"), pred("発送先数"), 出荷先数Tb.Text))
            アイテム数Tb.Text = If(pred.ContainsKey("アイテム数"), pred("アイテム数"), アイテム数Tb.Text)

            ' 抽出I_計Tb に I1〜I5 の合計を設定
            Dim sumI As Long = 0
            sumI += ToLongOrZero(抽出I_A1Tb.Text)
            sumI += ToLongOrZero(抽出I_A2Tb.Text)
            sumI += ToLongOrZero(抽出I_BTb.Text)
            sumI += ToLongOrZero(抽出I_CTb.Text)
            sumI += ToLongOrZero(抽出I_DTb.Text)
            抽出I_計Tb.Text = sumI.ToString()

            ' 抽出E_計Tb に E1〜E5 の合計を設定
            Dim sumE As Long = 0
            sumE += ToLongOrZero(抽出E_A1Tb.Text)
            sumE += ToLongOrZero(抽出E_A2Tb.Text)
            sumE += ToLongOrZero(抽出E_BTb.Text)
            sumE += ToLongOrZero(抽出E_CTb.Text)
            sumE += ToLongOrZero(抽出E_DTb.Text)
            抽出E_計Tb.Text = sumE.ToString()

            ' マトリクス表の埋め込み
            If AI予測EIQマトリクス表_複写Dgv Is Nothing Then Return
            AI予測EIQマトリクス表_複写Dgv.Rows.Clear()
            AI予測EIQマトリクス表_複写Dgv.Columns.Clear()
            ' 列ヘッダー
            AI予測EIQマトリクス表_複写Dgv.Columns.Add("GPLEランク", "GPLEランク")
            AI予測EIQマトリクス表_複写Dgv.Columns.Add("GPLI_A1", "GPLI_A1")
            AI予測EIQマトリクス表_複写Dgv.Columns.Add("GPLI_A2", "GPLI_A2")
            AI予測EIQマトリクス表_複写Dgv.Columns.Add("GPLI_B", "GPLI_B")
            AI予測EIQマトリクス表_複写Dgv.Columns.Add("GPLI_C", "GPLI_C")
            AI予測EIQマトリクス表_複写Dgv.Columns.Add("GPLI_D", "GPLI_D")
            AI予測EIQマトリクス表_複写Dgv.Columns.Add("横計", "横計")
            ' 列幅を0列は85、その他は83に設定
            For i As Integer = 0 To AI予測EIQマトリクス表_複写Dgv.Columns.Count - 1
                If i = 0 Then
                    AI予測EIQマトリクス表_複写Dgv.Columns(i).Width = 90
                Else
                    AI予測EIQマトリクス表_複写Dgv.Columns(i).Width = 83
                End If
            Next
            ' 行ラベル
            Dim labels As String() = {"GPLE_A1", "GPLE_A2", "GPLE_B", "GPLE_C", "GPLE_D"}
            For Each lbl As String In labels
                AI予測EIQマトリクス表_複写Dgv.Rows.Add(New Object() {lbl, "", "", "", "", "", ""})
            Next
            ' データ埋め込み
            Const dataRows As Integer = 5
            Const dataCols As Integer = 5
            While AI予測EIQマトリクス表_複写Dgv.Rows.Count < dataRows + 1
                AI予測EIQマトリクス表_複写Dgv.Rows.Add()
            End While
            Dim lastColIndex As Integer = AI予測EIQマトリクス表_複写Dgv.Columns.Count - 1
            Dim totalsRowIndex As Integer = dataRows
            For r As Integer = 0 To dataRows - 1
                For c As Integer = 1 To dataCols
                    Dim idx As Integer = r * dataCols + (c - 1)
                    Dim k As Integer = idx + 1
                    Dim fieldName As String = prefix & k.ToString("D2")
                    Dim value As String = ""
                    If pred.ContainsKey(fieldName) Then value = pred(fieldName)
                    AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value = value
                Next
            Next
            ' 横計
            For r As Integer = 0 To dataRows - 1
                Dim rowSum As Long = 0
                For c As Integer = 1 To dataCols
                    rowSum += ToLongOrZero(If(AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value, "").ToString())
                Next
                AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(lastColIndex).Value = rowSum.ToString()
            Next
            ' 縦計
            For c As Integer = 1 To dataCols
                Dim colSum As Long = 0
                For r As Integer = 0 To dataRows - 1
                    colSum += ToLongOrZero(If(AI予測EIQマトリクス表_複写Dgv.Rows(r).Cells(c).Value, "").ToString())
                Next
                AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells(c).Value = colSum.ToString()
            Next
            ' 最下行左のラベルと総合計
            AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells(0).Value = "縦計"
            Dim grandTotal As Long = 0
            For c As Integer = 1 To 5
                grandTotal += ToLongOrZero(If(AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells(c).Value, "").ToString())
            Next
            AI予測EIQマトリクス表_複写Dgv.Rows(totalsRowIndex).Cells(lastColIndex).Value = grandTotal.ToString()

            ' 書式設定: 右寄せ、ヘッダー中央、読み取り専用
            AI予測EIQマトリクス表_複写Dgv.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter
            AI予測EIQマトリクス表_複写Dgv.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
            For Each col As DataGridViewColumn In AI予測EIQマトリクス表_複写Dgv.Columns
                col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
                col.HeaderCell.Style.Alignment = DataGridViewContentAlignment.MiddleCenter
            Next
            AI予測EIQマトリクス表_複写Dgv.ReadOnly = True
            AI予測EIQマトリクス表_複写Dgv.RowHeadersVisible = False
        Catch ex As Exception
        End Try
    End Sub




#End Region

End Class
